🎯 系列目標:用 30 天時間,從零開始打造一個專屬輔大學生的課表生成 Chrome 擴充功能
👨💻 作者:輔大智慧資安 412580084
📅 Day 17:Chrome Extension 實作篇 - 組件協作與基礎消息傳遞
昨天我們學會了在網頁中注入「我的課表」按鈕,今天我們要深入學習如何讓 Content Script 與 Background Script 協同工作,建立基本的自動化課表生成流程!
今天我們要完成:
在前面的學習中,我們分別建立了三個主要組件:
我們採用「中央控制」模式:
Content Script ──────┐
▼
Background Script
▲
Popup Script ────────┘
模組 | 責任說明 |
---|---|
Content Script | 1. 注入按鈕到網頁2. 監聽用戶點擊3. 發送消息給 Background4. 顯示處理結果給用戶 |
Background Script | 1. 接收來自 Content/Popup 的消息2. 執行核心業務邏輯3. 管理分頁和資料4. 協調各組件工作 |
Popup Script | 1. 提供用戶操作介面2. 發送消息給 Background3. 顯示處理狀態和結果 |
在開始實作之前,我們先了解整個課表生成的基本流程:
flowchart TD
A[用戶點擊「我的課表」按鈕] --> B[Content Script 發送消息]
B --> C[Background Script 接收處理]
C --> D[執行基本處理流程]
D --> E[回應處理結果]
E --> F[顯示結果給用戶]
這是整個系統的核心,負責接收和處理消息:
// Background Script 的基本消息監聽器
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('📨 Background 收到消息:', request);
// 檢查消息類型
if (request.action === 'generateSchedule') {
console.log('🚀 開始課表生成流程');
handleBasicScheduleGeneration(request, sendResponse);
return true; // 保持消息通道開放
}
// 其他消息類型處理...
console.log('❓ 未知的消息類型:', request.action);
sendResponse({ success: false, error: '未知的操作類型' });
});
說明:
onMessage.addListener
監聽來自其他組件的消息request.action
用來區分不同的操作類型return true
保持消息通道開放,允許非同步回應將課表生成流程簡化為基本版本,透過chrome.tabs.create()
可以開啟新分頁:
// 基本的課表生成處理函數
async function handleBasicScheduleGeneration(request, sendResponse) {
try {
console.log('🗺️ 開始基本課表生成流程');
// 步驟 1:記錄請求資訊
console.log('📝 步驟 1: 記錄用戶請求');
console.log('來源頁面:', request.data.url);
console.log('請求時間:', request.data.timestamp);
// 步驟 2:模擬基本處理
console.log('⚙️ 步驟 2: 執行基本處理');
await simulateProcessing();
// 步驟 3:開啟結果頁面
console.log('🎉 步驟 3: 開啟課表結果頁面');
chrome.tabs.create({ url: 'schedule.html' });
// 成功回應
sendResponse({
success: true,
message: '基本課表生成流程完成',
data: {
processedAt: new Date().toISOString(),
fromUrl: request.data.url
}
});
} catch (error) {
console.error('❌ 課表生成流程失敗:', error);
sendResponse({
success: false,
error: error.message,
timestamp: new Date().toISOString()
});
}
}
// 模擬處理時間
function simulateProcessing() {
return new Promise(resolve => {
console.log('⏳ 模擬處理中...');
setTimeout(() => {
console.log('✅ 模擬處理完成');
resolve();
}, 2000); // 模擬 2 秒處理時間
});
}
由於 Background Script 現在處理所有複雜邏輯,Content Script 可以專注於使用者介面:
// 優化的按鈕點擊處理
function handleScheduleButtonClick(event) {
event.preventDefault();
console.log('📊 「我的課表」按鈕被點擊');
const button = event.target;
const originalText = button.textContent;
// 更新按鈕狀態
setButtonProcessing(button);
// 發送消息給 background script
chrome.runtime.sendMessage({
action: 'generateSchedule',
source: 'webpage',
data: {
url: window.location.href,
timestamp: new Date().toISOString()
}
}, (response) => {
// 處理回應並恢復按鈕狀態
handleScheduleResponse(button, originalText, response);
});
}
// 設定按鈕處理中狀態
function setButtonProcessing(button) {
button.textContent = '⏳ 處理中...';
button.style.pointerEvents = 'none';
button.style.opacity = '0.7';
}
// 處理背景腳本的回應
function handleScheduleResponse(button, originalText, response) {
// 恢復按鈕狀態
button.textContent = originalText;
button.style.pointerEvents = 'auto';
button.style.opacity = '1';
if (response && response.success) {
console.log('✅ 課表生成成功');
showNotification('課表生成成功!正在開啟結果頁面...', 'success');
} else {
console.error('❌ 課表生成失敗:', response?.error);
showNotification('課表生成失敗:' + (response?.error || '未知錯誤'), 'error');
}
}
background.js
:加入今天學習的基本流程控制邏輯content.js
:優化按鈕互動處理在 Chrome 擴充功能管理頁面重新載入
https://portal.fju.edu.tw/student/
應該會開啟 schedule.html
頁面(目前尚未建立因此以下畫面為正常的)
檢查日誌是否為以下情況即成功 (在schedule.html
中按下 F12
檢查)
Day 18 我們將深入學習 Background Script 分頁控制機制